<?php

namespace Illuminate\Cache;

use Illuminate\Contracts\Cache\Repository as Cache;
use Illuminate\Support\InteractsWithTime;

/**
 * 节流器类
 */
class RateLimiter
{
    use InteractsWithTime;

    /**
     * 缓存实例
     *
     * @var \Illuminate\Contracts\Cache\Repository
     */
    protected $cache;

    /**
     * 创建一个新的节流器实例
     *
     * @param  \Illuminate\Contracts\Cache\Repository $cache
     * @return void
     */
    public function __construct(Cache $cache)
    {
        $this->cache = $cache;
    }

    /**
     * 确认给定的键 Key 是否被“访问”了太多次
     *
     * @param  string $key 键
     * @param  int $maxAttempts 最大次数
     * @return bool
     */
    public function tooManyAttempts($key, $maxAttempts)
    {
        if ($this->attempts($key) >= $maxAttempts) {
            if ($this->cache->has($key . ':timer')) {
                return true;
            }

            $this->resetAttempts($key);
        }

        return false;
    }

    /**
     * 在给定衰减时间内，递增指定键的计数器
     *
     * @param  string $key 键
     * @param  int $decaySeconds 衰减时间
     * @return int
     */
    public function hit($key, $decaySeconds = 60)
    {
        $this->cache->add(
            $key . ':timer', $this->availableAt($decaySeconds), $decaySeconds
        );

        $added = $this->cache->add($key, 0, $decaySeconds);

        $hits = (int)$this->cache->increment($key);

        // 如果 key 值之前已经被添加过，但是没有计数，就更新计数器和衰减时间
        if (!$added && $hits == 1) {
            $this->cache->put($key, 1, $decaySeconds);
        }

        return $hits;
    }

    /**
     * 获取给定键 Key 的已尝试次数
     *
     * @param  string $key 键
     * @return mixed
     */
    public function attempts($key)
    {
        return $this->cache->get($key, 0);
    }

    /**
     * 重置给定键的尝试次数
     *
     * @param  string $key 键
     * @return mixed
     */
    public function resetAttempts($key)
    {
        return $this->cache->forget($key);
    }

    /**
     * 获取给定键的剩余重试次数
     *
     * @param  string $key 键
     * @param  int $maxAttempts 最大可尝试次数
     * @return int
     */
    public function retriesLeft($key, $maxAttempts)
    {
        $attempts = $this->attempts($key);

        return $maxAttempts - $attempts;
    }

    /**
     * 清除指定 Key 的命中次数和计时器
     *
     * @param  string $key
     * @return void
     */
    public function clear($key)
    {
        $this->resetAttempts($key);

        $this->cache->forget($key . ':timer');
    }

    /**
     * 获取获取指定 Key 的剩余有效期（秒）
     *
     * @param  string $key
     * @return int
     */
    public function availableIn($key)
    {
        return $this->cache->get($key . ':timer') - $this->currentTime();
    }
}
